import CryptoJS from 'crypto-js'
import Formatter from './Formatter';

const DB_NAME = 'app_file_db';
const DB_VERSION = 1; // Use a long long for this value (don't use a float)
const DB_STORE_NAME = 'app_file';

let dbPromise = null

/**
 * @returns {Promise<IDBDatabase>}
 */
async function getDb() {
    if (dbPromise) return dbPromise
    dbPromise = new Promise((resolve, reject) => {
        var req = indexedDB.open(DB_NAME, DB_VERSION);
        req.onsuccess = (evt) => resolve(evt.target.result)
        req.onerror = (evt) => reject(evt)
        req.onupgradeneeded = (evt) => {
            evt.currentTarget.result
                .createObjectStore(DB_STORE_NAME, { keyPath: 'key' })
                .createIndex('key', 'key', { unique: true })
        }
    })
    return dbPromise
}

class IndexedDBStore {

    static async get(key) {
        if (!key) return null
        let db = await getDb()
        return new Promise((resolve, reject) => {
            let req = db.transaction([DB_STORE_NAME]).objectStore(DB_STORE_NAME).get(key)
            req.onsuccess = (event) => resolve(event.target.result && event.target.result.file)
            req.onerror = (event) => reject(event)
        })
    }

    static async put(key, file) {
        let db = await getDb()
        let transaction = db.transaction([DB_STORE_NAME], "readwrite")
        return new Promise((resolve, reject) => {
            let objectStore = transaction.objectStore(DB_STORE_NAME)
            let req = objectStore.put({
                key: key,
                file: file
            })
            req.onsuccess = (event) => resolve(event)
            req.onerror = (event) => reject(event)
        })
    }

    static async add(key, file) {
        let db = await getDb()
        let transaction = db.transaction([DB_STORE_NAME], "readwrite")
        return new Promise((resolve, reject) => {
            let objectStore = transaction.objectStore(DB_STORE_NAME)
            let req = objectStore.add({
                key: key,
                file: file
            })
            req.onsuccess = (event) => resolve(event)
            req.onerror = (event) => reject(event)
        })
    }

    static async clear() {
        let db = await getDb()
        let transaction = db.transaction([DB_STORE_NAME], "readwrite")
        return new Promise((resolve, reject) => {
            let objectStore = transaction.objectStore(DB_STORE_NAME)
            let req = objectStore.clear()
            req.onsuccess = (event) => resolve(event)
            req.onerror = (event) => reject(event)
        })
    }

    static async delete(key) {
        let db = await getDb()
        let transaction = db.transaction([DB_STORE_NAME], "readwrite")
        return new Promise((resolve, reject) => {
            let objectStore = transaction.objectStore(DB_STORE_NAME)
            let req = objectStore.delete(key)
            req.onsuccess = (event) => resolve(event)
            req.onerror = (event) => reject(event)
        })
    }

    static md5(data) {
        if (typeof data == 'string') {
            return CryptoJS.MD5(data).toString()
        } else {
            return CryptoJS.MD5(CryptoJS.lib.WordArray.create(data)).toString()
        }
    }

    static md5JSON(data) {
        this.md5(Formatter.jsonStr(data))
    }

    static async listKey() {
        let db = await getDb()
        let transaction = db.transaction([DB_STORE_NAME], "readwrite")
        return new Promise((resolve, reject) => {
            let objectStore = transaction.objectStore(DB_STORE_NAME)
            let req = objectStore.getAllKeys()
            req.onsuccess = (event) => resolve(event.target.result)
            req.onerror = (event) => reject(event)
        })
    }
}

export default IndexedDBStore